/**HEADER********************************************************************
* 
* Copyright (c) 2011 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: timer_mtim.c$
* $Version : 3.8.2.1$
* $Date    : Feb-13-2012$
*
* Comments:
*
*   This file contains timer functions for use with a MTIM.
*
*END************************************************************************/

#include <mqx.h>
#include <bsp.h>
#include "timer_mtim.h"


#define MTIMSC_TSTP_MASK  (1<<4)
#define MTIMSC_TRST_MASK  (1<<5)
#define MTIMSC_TOIE_MASK  (1<<6)
#define MTIMSC_TOF_MASK   (1<<7)


typedef struct mtim_struct
{
    uint_8 MTIMSC;   /* Status/control Register */
    uint_8 MTIMCLK;  /* Clock Configuration Register */
    uint_8 MTIMCNT;  /* Counter Register */
    uint_8 MTIMMOD;  /* Modulo Register */      
} MTIM_STRUCT, _PTR_ MTIM_STRUCT_PTR;


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _mtim_init_freq
* Returned Value  : the clock rate for the timer (ns per hw tick)
* Comments        :
*   this function will set up a timer to interrupt
*
*END*---------------------------------------------------------------------*/

uint_32 _mtim_init_freq
    (
        /* [IN] the timer to initialize */
        uint_8    timer,

        /* [IN] ticks per second */
        uint_32   tickfreq,

        /* [IN] input clock speed in Hz */
        uint_32   clk,

        /* [IN] unmask the timer after initializing */
        boolean   unmask_timer
    )
{
    int_32 rate, postscale, prescale = 0;

    volatile MTIM_STRUCT _PTR_  mtim_ptr;
   
    mtim_ptr = _bsp_get_mtim_base_address(timer);
    if (mtim_ptr == NULL) return 0;

    /*
     * MTIM is able to count up to 65536 including HW prescaler
     * anything above this has to be post-scaled in software
     */     
    postscale = ((clk / tickfreq) + 0xFFFF) / 0x10000;
    clk /= postscale; /* clock correction according to sw postscaler (if any) */

    rate = (clk / tickfreq);
    while (rate > (0xFF+1)) {
        prescale += 1;  /* divides clock in half */
        clk >>= 1;
        rate = (clk / tickfreq);
    }

    _bsp_mtim_clk_en(timer); /* enable clock to the MTIM */

    /* reset and stop counter */
    mtim_ptr->MTIMSC = MTIMSC_TRST_MASK | MTIMSC_TSTP_MASK;

    /* setup software postscaler */
    _postscaler_init(&_bsp_mtim_postscaler[timer], postscale);

    /* set registers */
    mtim_ptr->MTIMCLK = (uint_8) prescale;  
    mtim_ptr->MTIMMOD = (uint_8) rate - 1;

    /* start counter and enable interrupt (if desired) */
    mtim_ptr->MTIMSC =  (unmask_timer ? MTIMSC_TOIE_MASK : 0);

    /* return number of hw-ticks per MQX tick (including sw postscaler) */
    return rate * postscale;
}


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _mtim_get_hwticks
* Returned Value  : Number of ticks
* Comments        :
*   This function will return the number of ticks elapsed since the last
* timer tick.
*
*END*---------------------------------------------------------------------*/

uint_32 _mtim_get_hwticks
   (
      /* [IN] the timer to clear */
      pointer parameter
   )
{
    uint_8  timer = (uint_8)parameter;
    uint_32 ticks;
    uint_32 modulo;

    volatile MTIM_STRUCT _PTR_  mtim_ptr;
   
    mtim_ptr = _bsp_get_mtim_base_address(timer);
    if (mtim_ptr == NULL) return 0;

    modulo = mtim_ptr->MTIMMOD + 1;
    ticks = mtim_ptr->MTIMCNT;

    if ((mtim_ptr->MTIMSC & MTIMSC_TOF_MASK) != 0) {
        /* Another full TICK period has expired since we handled the last timer interrupt.
        We need to read the counter again, since the wrap may have
        occurred between the previous read and the checking of the overflow bit. */
        ticks = mtim_ptr->MTIMCNT + modulo;
    }

    /* actual ticks in MTIMCNT + what we have missed because of sw prescaller */ 
    return ticks + modulo * _bsp_mtim_postscaler[timer].counter;
}


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _mtim_mask_int
* Returned Value  :
* Comments        :
*   This function will mask interrupt of given timer
*
*END*---------------------------------------------------------------------*/

void _mtim_mask_int
    (
        /* [IN] the timer to use */
        uint_8    timer
    )
{
    volatile MTIM_STRUCT _PTR_  mtim_ptr;
   
    mtim_ptr = _bsp_get_mtim_base_address(timer);
    if (mtim_ptr == NULL) return;

    mtim_ptr->MTIMSC &= ~((uint_8)MTIMSC_TOIE_MASK);
}


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _mtim_unmask_int
* Returned Value  :
* Comments        :
*   This function will unmask interrupt of given timer
*
*END*---------------------------------------------------------------------*/

void _mtim_unmask_int
    (
        /* [IN] the timer to use */
        uint_8    timer
    )
{
    volatile MTIM_STRUCT _PTR_  mtim_ptr;
   
    mtim_ptr = _bsp_get_mtim_base_address(timer);
    if (mtim_ptr == NULL) return;

    /* clear interrupt flag first, then enable interrupt */
    mtim_ptr->MTIMSC = (mtim_ptr->MTIMSC & ~((uint_8)MTIMSC_TOF_MASK)) | MTIMSC_TOIE_MASK;
}


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _mtim_clear_int
* Returned Value  :
* Comments        :
*   This function will clear interrupt flag of given timer
*
*END*---------------------------------------------------------------------*/

void _mtim_clear_int
    (
        /* [IN] the timer to use */
        uint_8    timer
    )
{
    volatile MTIM_STRUCT _PTR_  mtim_ptr;
   
    mtim_ptr = _bsp_get_mtim_base_address(timer);
    if (mtim_ptr == NULL) return;

    mtim_ptr->MTIMSC &= ~((uint_8)MTIMSC_TOF_MASK);
}
